package controllers.GestionTratamientoDatos.Metodos.Verificacion.Metodos;

import controllers.GestionTratamientoDatos.Metodos.Verificacion.MetodoVerificacionBase;

import models.Hidregistro;
import java.util.List;
import java.util.ArrayList;

/**
 * Created with IntelliJ IDEA.
 * User: Daniel
 * Date: 07/02/13
 * Time: 12:22 PM
 * To change this template use File | Settings | File Templates.
 */
public class VerificacionMediasMoviles extends MetodoVerificacionBase {

    public int numeroDiasIntervalo;
    public int numeroDiasFilaMatriz;

    public VerificacionMediasMoviles(List<Hidregistro> serieEntrada) {
        super(serieEntrada);
    }

    public VerificacionMediasMoviles(List<Hidregistro> serieEntrada, int numeroDiasIntervalo, int numeroDiasFilaMatriz) {
        super(serieEntrada);
        this.numeroDiasIntervalo = numeroDiasIntervalo;
        this.numeroDiasFilaMatriz = numeroDiasFilaMatriz;
    }

    @Override
    public void Verificar()
    {
        double []mediasMoviles = MediasMoviles();
        double []desviacionesStandar = DesviacionesStandar(mediasMoviles);
        DeterminarDatosVerificados(mediasMoviles, desviacionesStandar);
    }

    @Override
    public void VerificarUltimoAnho()
    {
        double []mediasMoviles = MediasMoviles();
        double []desviacionesStandar = DesviacionesStandar(mediasMoviles);
        DeterminarDatosVerificadosUltimoAnho(mediasMoviles, desviacionesStandar);
    }

    public double[] MediasMoviles()
    {
        int margen = DeterminarMargen(numeroDiasIntervalo);
        double []arregloCompletado = CompletarArreglo(getSerieEntrada(), margen);
        double []mediasMovilesAcumulado = CalcularMediasMoviles(arregloCompletado,margen);
        double [][]matrizMediasMoviles = ConvertirArregloMatriz(mediasMovilesAcumulado, numeroDiasFilaMatriz);
        double []matrizSuma = SumarFilasMatriz(matrizMediasMoviles);
        int numeroAnhos = getSerieEntrada().size() / numeroDiasFilaMatriz;
        double []mediasMoviles = PromediarArreglo(matrizSuma, numeroDiasIntervalo * numeroAnhos);

        return mediasMoviles;
    }

    public double[] DesviacionesStandar(double []mediasMoviles)
    {
        double [][]matrizRestasDatosMedias = MatrizRestasDatoMedias(mediasMoviles);
        double []arregloDesviaciones = ConvertirMatrizArreglo(matrizRestasDatosMedias);
        double []arregloCompletado = CompletarArreglo(arregloDesviaciones, DeterminarMargen(numeroDiasIntervalo));
        double []desviacionesStandarAcumuladas =  CalcularDesviacionesStandarAcumuladas(arregloCompletado);
        double [][]matrizDesviaciones = ConvertirArregloMatriz(desviacionesStandarAcumuladas, numeroDiasFilaMatriz);
        double []matrizSuma = SumarFilasMatriz(matrizDesviaciones);

        int numeroAnhos = getSerieEntrada().size() / numeroDiasFilaMatriz;
        double []varianzas = PromediarArreglo(matrizSuma, numeroDiasIntervalo * numeroAnhos);
        double []desviacionesStandar = RaizArreglo(varianzas);

        return desviacionesStandar;
    }

    public void DeterminarDatosVerificados(double []mediasMoviles, double []desviacionesStandar)
    {
        List<Hidregistro> hidregistros = new ArrayList<Hidregistro>();
        double [][]matrizValoresEntrada = ConvertirListaMatriz(getSerieEntrada(), numeroDiasFilaMatriz);

        int factor = 2;
        for (int i = 0; i < matrizValoresEntrada.length; i++)
        {
            for (int j = 0; j < matrizValoresEntrada[i].length; j++)
            {
                if(Math.abs(matrizValoresEntrada[i][j] - mediasMoviles[j]) <= factor * desviacionesStandar[j])
                {
                    //hidregistros.add(getSerieEntrada().get(i * matrizValoresEntrada[i].length + j));
                }
                else
                {
                    hidregistros.add(getSerieEntrada().get(i * matrizValoresEntrada[i].length + j));
                }
            }
        }
        setSerieSalida(hidregistros);
    }

    public void DeterminarDatosVerificadosUltimoAnho(double []mediasMoviles, double []desviacionesStandar)
    {
        List<Hidregistro> hidregistros = new ArrayList<Hidregistro>();
        double [][]matrizValoresEntrada = ConvertirListaMatriz(getSerieUltimoAnho(), numeroDiasFilaMatriz);

        int factor = 2;
        for (int i = 0; i < matrizValoresEntrada.length; i++)
        {
            for (int j = 0; j < matrizValoresEntrada[i].length; j++)
            {
                if(Math.abs(matrizValoresEntrada[i][j] - mediasMoviles[j]) <= factor * desviacionesStandar[j])
                {
                    //hidregistros.add(getSerieEntrada().get(i * matrizValoresEntrada[i].length + j));
                }
                else
                {
                    hidregistros.add(getSerieUltimoAnho().get(i * matrizValoresEntrada[i].length + j));
                }
            }
        }
        setSerieSalida(hidregistros);
    }

    public int DeterminarMargen(int numeroDias)
    {
        int margen = 0;
        if(numeroDias % 2 == 0)
        {
            margen = numeroDias / 2;
        }
        else
        {
            margen = (numeroDias - 1) / 2;
        }

        return margen;
    }

    public double[] CalcularMediasMoviles(double []arregloCompletado, int margen)
    {
        double []mediasMoviles = new double[arregloCompletado.length - 2 * margen];

        for (int i = 0; i < arregloCompletado.length - 2 * margen; i++)
        {
            double media = 0;

            for (int j = 1; j < margen + 1; j++)
            {
                media += arregloCompletado[i + margen - j];
                media += arregloCompletado[i + margen + j];
            }

            media += arregloCompletado[i + margen];

            mediasMoviles[i] = media;
        }

        return mediasMoviles;
    }

    public double[] SumarFilasMatriz(double [][]arregloEntrada)
    {
        double []arregloResultante = new double[arregloEntrada[0].length];

        for (int i = 0; i < arregloEntrada[0].length; i++)
        {
            arregloResultante[i] = 0;
            for (int j = 0; j < arregloEntrada.length; j++)
            {
                 arregloResultante[i] += arregloEntrada[j][i];
            }
        }

        return arregloResultante;
    }

    public double[] PromediarArreglo(double []arregloEntrada, int numeroElementos)
    {
        for (int i = 0; i < arregloEntrada.length; i++)
        {
            arregloEntrada[i] /= numeroElementos;
        }
        return arregloEntrada;
    }

    public double[][] MatrizRestasDatoMedias(double[] mediasMoviles)
    {

        double [][]matrizRestasDatosMedias = ConvertirListaMatriz(getSerieEntrada(), numeroDiasFilaMatriz);

        for (int i = 0; i < matrizRestasDatosMedias.length; i++)
        {
            for (int j = 0; j < matrizRestasDatosMedias[0].length; j++)
            {
                matrizRestasDatosMedias[i][j] = Math.pow((matrizRestasDatosMedias[i][j] - mediasMoviles[j]),2);
            }
        }
        return matrizRestasDatosMedias;
    }

    public double[] CalcularDesviacionesStandarAcumuladas(double []desviacionesStandarCompletadas)
    {
        int margen = DeterminarMargen(numeroDiasIntervalo);
        double []acumuladas = new double[desviacionesStandarCompletadas.length - 2 * margen];

        for (int i = 0; i < desviacionesStandarCompletadas.length - 2 * margen; i++)
        {
            double media = 0;
            for (int j = 1; j < margen + 1; j++)
            {
                media += desviacionesStandarCompletadas[i + margen - j];
                media += desviacionesStandarCompletadas[i + margen + j];
            }

            media += desviacionesStandarCompletadas[i + margen];
            acumuladas[i] = media;
        }

        return acumuladas;
    }

    public double[] RaizArreglo(double []arreglo)
    {
        for (int i = 0; i < arreglo.length; i++)
        {
            arreglo[i] = Math.sqrt(arreglo[i]);
        }
        return arreglo;
    }

    public int getNumeroDiasIntervalo() {
        return numeroDiasIntervalo;
    }

    public void setNumeroDiasIntervalo(int numeroDiasIntervalo) {
        this.numeroDiasIntervalo = numeroDiasIntervalo;
    }

    public int getNumeroDiasFilaMatriz() {
        return numeroDiasFilaMatriz;
    }

    public void setNumeroDiasFilaMatriz(int numeroDiasFilaMatriz) {
        this.numeroDiasFilaMatriz = numeroDiasFilaMatriz;
    }
}
